package core;

import java.security.SecureRandom;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ThreadLocalRandom;

/**
 * 随机工具类
 * <p>
 * 提供各种随机数生成方法，包括基础数据类型随机数、字符串随机数、集合随机元素等。
 * </p>
 */
public class RandomUtil {

    /**
     * 用于随机选的数字
     */
    public static final String BASE_NUMBER = "0123456789";
    /**
     * 用于随机选的字符（小写）
     */
    public static final String BASE_CHAR = "abcdefghijklmnopqrstuvwxyz";
    /**
     * 用于随机选的字符和数字（小写）
     */
    public static final String BASE_CHAR_NUMBER_LOWER = BASE_CHAR + BASE_NUMBER;
    /**
     * 用于随机选的字符和数字（包括大写和小写字母）
     */
    public static final String BASE_CHAR_NUMBER = BASE_CHAR.toUpperCase() + BASE_CHAR_NUMBER_LOWER;

    // region ----- get or create Random

    /**
     * 获取随机数生成器对象<br>
     * ThreadLocalRandom是JDK 7之后提供并发产生随机数，能够解决多个线程发生的竞争争夺。
     *
     * @return {@link ThreadLocalRandom}
     */
    public static ThreadLocalRandom getRandom() {
        return ThreadLocalRandom.current();
    }

    /**
     * 创建{@link SecureRandom}，类提供加密的强随机数生成器 (RNG)
     *
     * @param seed 自定义随机种子
     * @return {@link SecureRandom}
     */
    public static SecureRandom createSecureRandom(final byte[] seed) {
        return (null == seed) ? new SecureRandom() : new SecureRandom(seed);
    }

    /**
     * 获取SHA1PRNG的{@link SecureRandom}，类提供加密的强随机数生成器 (RNG)
     *
     * @return {@link SecureRandom}
     */
    public static SecureRandom getSecureRandom() {
        return getSecureRandom(null);
    }

    /**
     * 获取SHA1PRNG的{@link SecureRandom}，类提供加密的强随机数生成器 (RNG)
     *
     * @param seed 随机数种子
     * @return {@link SecureRandom}
     */
    public static SecureRandom getSecureRandom(final byte[] seed) {
        return createSecureRandom(seed);
    }

    // endregion

    // region ----- random int

    /**
     * 获得随机数int值
     *
     * @return 随机数
     */
    public static int randomInt() {
        return getRandom().nextInt();
    }

    /**
     * 获得指定范围内的随机数 [0, limit)
     *
     * @param limit 限制随机数的范围，不包括这个值
     * @return 随机数
     */
    public static int randomInt(int limit) {
        return getRandom().nextInt(limit);
    }

    /**
     * 获得指定范围内的随机数[min, max)
     *
     * @param min 最小数（包含）
     * @param max 最大数（不包含）
     * @return 随机数
     */
    public static int randomInt(int min, int max) {
        return getRandom().nextInt(min, max);
    }

    // endregion

    // region ----- random long

    /**
     * 获得随机数long值
     *
     * @return 随机数
     */
    public static long randomLong() {
        return getRandom().nextLong();
    }

    /**
     * 获得指定范围内的随机数 [0, limit)
     *
     * @param limit 限制随机数的范围，不包括这个值
     * @return 随机数
     */
    public static long randomLong(long limit) {
        return getRandom().nextLong(limit);
    }

    /**
     * 获得指定范围内的随机数[min, max)
     *
     * @param min 最小数（包含）
     * @param max 最大数（不包含）
     * @return 随机数
     */
    public static long randomLong(long min, long max) {
        return getRandom().nextLong(min, max);
    }

    // endregion

    // region ----- random double

    /**
     * 获得随机数double值
     *
     * @return 随机数
     */
    public static double randomDouble() {
        return getRandom().nextDouble();
    }

    /**
     * 获得指定范围内的随机数 [0, limit)
     *
     * @param limit 限制随机数的范围，不包括这个值
     * @return 随机数
     */
    public static double randomDouble(double limit) {
        return getRandom().nextDouble(limit);
    }

    /**
     * 获得指定范围内的随机数[min, max)
     *
     * @param min 最小数（包含）
     * @param max 最大数（不包含）
     * @return 随机数
     */
    public static double randomDouble(double min, double max) {
        return getRandom().nextDouble(min, max);
    }

    // endregion

    // region ----- random boolean

    /**
     * 随机获得boolean值
     *
     * @return 随机boolean值
     */
    public static boolean randomBoolean() {
        return randomInt(2) == 1;
    }

    // endregion

    // region ----- random string

    /**
     * 随机获取一个字符串
     *
     * @param baseString 随机字符选取的样本
     * @param length     字符串的长度
     * @return 随机字符串
     */
    public static String randomString(String baseString, int length) {
        if (ObjectUtil.isEmpty(baseString) || length <= 0) {
            return "";
        }

        final StringBuilder sb = new StringBuilder(length);
        final ThreadLocalRandom random = getRandom();
        for (int i = 0; i < length; i++) {
            sb.append(baseString.charAt(random.nextInt(baseString.length())));
        }
        return sb.toString();
    }

    /**
     * 随机获得一个数字字符串，长度为10位
     *
     * @return 随机数字字符串
     */
    public static String randomNumbers() {
        return randomString(BASE_NUMBER, 10);
    }

    /**
     * 随机获得一个数字字符串
     *
     * @param length 字符串长度
     * @return 随机数字字符串
     */
    public static String randomNumbers(int length) {
        return randomString(BASE_NUMBER, length);
    }

    /**
     * 随机获得一个字母字符串（小写）
     *
     * @param length 字符串长度
     * @return 随机字母字符串
     */
    public static String randomLetter(int length) {
        return randomString(BASE_CHAR, length);
    }

    /**
     * 随机获得一个字母和数字字符串（小写）
     *
     * @param length 字符串长度
     * @return 随机字母和数字字符串
     */
    public static String randomLetterAndNumber(int length) {
        return randomString(BASE_CHAR_NUMBER_LOWER, length);
    }

    /**
     * 随机获得一个字母和数字字符串（包含大小写）
     *
     * @param length 字符串长度
     * @return 随机字母和数字字符串
     */
    public static String randomCharAndNumber(int length) {
        return randomString(BASE_CHAR_NUMBER, length);
    }

    // endregion

    // region ----- random element

    /**
     * 随机获得列表中的元素
     *
     * @param <T>  元素类型
     * @param list 列表
     * @return 随机元素
     */
    public static <T> T randomEle(List<T> list) {
        if (CollectionUtil.isEmpty(list)) {
            return null;
        }
        return list.get(randomInt(list.size()));
    }

    /**
     * 随机获得数组中的元素
     *
     * @param <T>   元素类型
     * @param array 列表
     * @return 随机元素
     */
    public static <T> T randomEle(T[] array) {
        if (ObjectUtil.isEmpty(array)) {
            return null;
        }
        return array[randomInt(array.length)];
    }

    /**
     * 随机获得列表中的一定量元素
     *
     * @param <T>   元素类型
     * @param list  列表
     * @param count 随机取出的个数
     * @return 随机元素
     */
    public static <T> List<T> randomEles(List<T> list, int count) {
        if (CollectionUtil.isEmpty(list) || count <= 0) {
            return new ArrayList<>();
        }

        final List<T> result = new ArrayList<>(count);
        final ThreadLocalRandom random = getRandom();
        for (int i = 0; i < count; i++) {
            result.add(list.get(random.nextInt(list.size())));
        }
        return result;
    }

    // endregion

    // region ----- random uuid

    /**
     * 生成UUID
     *
     * @return UUID字符串
     */
    public static String randomUUID() {
        return StringUtil.uuid();
    }

    // endregion
}